/****************************************************
	文件：MathHT.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2020/7/24 18:3:8
	功能：数学工具类
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;

namespace HTSpider
{
    public static class MathHT
    {
        public enum NormDistCalcuType
        {
            TaylorExpansion = 0,
            NormMap = 1,
        }
        public const double PI = 3.1415926;
        /// <summary>
        /// 正态分布表
        /// </summary>
        public static Dictionary<double, double> NORM_MAP = new Dictionary<double, double>();
        public static void Init()
        {
            NORM_MAP.Add(0.00f, 0.5000);
            NORM_MAP.Add(0.01f, 0.5040);
            NORM_MAP.Add(0.02f, 0.5080);
            NORM_MAP.Add(0.03f, 0.5120);
            NORM_MAP.Add(0.04f, 0.5160);
            NORM_MAP.Add(0.05f, 0.5199);
            //TOADD
        }
        /// <summary>
        /// 获取小数点后第 count 位
        /// </summary>
        public static int GetAfterPointNum(double num, int count)
        {
            double n = num * Math.Pow(10, count) % 10;
            return (int)Math.Floor(n);
        }
        /// <summary>
        /// 阶乘
        /// </summary>
        public static int Factorial(int num)
        {
            if (num < 0)
            {
                Logger.Error("阶乘出错，num：" + num);
                return 1;
            }
            if (num == 0) return 1;
            int ret = 1;
            for (int i = num; i >= 1; i--)
            {
                ret *= i;
            }
            return ret;
        }
        /// <summary>
        /// 均值
        /// </summary>
        public static double Average(IEnumerable<double> dataArray)
        {
            double ret = 0.0f;
            int count = 0;
            foreach (var item in dataArray)
            {
                ret += item;
                count++;
            }
            return ret / count * 1.0f;
        }
        /// <summary>
        /// 方差
        /// </summary>
        public static double Dev(IEnumerable<double> dataArray)
        {
            double average = Average(dataArray);
            double ret = 0.0f;
            int count = 0;
            foreach (var item in dataArray)
            {
                ret += Math.Pow(item - average, 2);
                count++;
            }
            return ret / (count - 1); //自由度 n-1
        }
        /// <summary>
        /// 标准差
        /// </summary>
        public static double Standard_Dev(IEnumerable<double> dataArray)
        {
            return Math.Sqrt(Dev(dataArray));
        }
        /// <summary>
        /// 计算落在区间 bottom 到 top 之间的概率（假设数据服从正态分布）
        /// </summary>
        /// <param name="bottom"></param>
        /// <param name="top"></param>
        /// <param name="average"></param>
        /// <param name="stdev"></param>
        /// <param name="calcuType"></param>
        /// <returns></returns>
        public static double Norm_Dist(double bottom, double top, double average, double stdev, NormDistCalcuType calcuType = NormDistCalcuType.TaylorExpansion)
        {
            return Norm_Dist_Left(top, average, stdev, calcuType) - Norm_Dist_Left(top, average, stdev, calcuType);
        }
        /// <summary>
        /// 计算落在 num 左边的概率（假设数据服从正态分布）
        /// </summary>
        /// <param name="num"></param>
        /// <param name="average"></param>
        /// <param name="stdev"></param>
        /// <param name="calcuType"></param>
        /// <returns></returns>
        public static double Norm_Dist_Left(double num, double average, double stdev, NormDistCalcuType calcuType = NormDistCalcuType.TaylorExpansion)
        {
            double x = (num - average) / stdev;
            double ret = 0.0f;
            switch (calcuType)
            {
                case NormDistCalcuType.TaylorExpansion: //方法一：泰勒展开
                    ret = Norm_Dist_Left_TaylorExpansion(x, 30); //展开30阶，精度足够
                    break;
                case NormDistCalcuType.NormMap: //方法二：正态分布表
                    ret = Norm_Dist_Left_NormMap(x);
                    break;
                default:
                    break;
            }
            return ret;
        }
        /// <summary>
        /// 使用泰勒展开求解 Norm_Dist （正态分布概率密度函数积分）
        /// </summary>
        /// <param name="x"></param>
        /// <param name="n">多项式个数，泰勒展开阶数</param>
        /// <returns></returns>
        private static double Norm_Dist_Left_TaylorExpansion(double x, int n)
        {
            double ret = 0.0f;
            for (int i = 0; i < n; i++)
            {
                ret += Math.Pow(-1, i) * Math.Pow(x, 2 * i + 1) / (Factorial(i) * Math.Pow(2, i) * (2 * i + 1));
            }
            ret = ret / Math.Sqrt(2 * PI) + 0.5;
            return ret >= 1 ? 1 : ret;
        }
        /// <summary>
        /// 使用正态分布表求解 Norm_Dist （正态分布概率密度函数积分）
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private static double Norm_Dist_Left_NormMap(double x)
        {
            double ret = 0.0f;
            double right = 0.00f;
            double delta = 0.00f;
            foreach (var kv in NORM_MAP)
            {
                delta = kv.Key - x;
                if (delta >= 0.0f && delta <= 0.01f)
                {
                    right = kv.Key;
                    break;
                }
            }
            double left = right - 0.01f <= 0.00f ? 0.00f : right - 0.01f;
            double ratio = (x - left) / 0.01f;
            ret = NORM_MAP[left] + ratio * (NORM_MAP[left] - NORM_MAP[right]);
            return ret >= 1 ? 1 : ret;
        }
    }
}
